﻿/* Copyright (C) 2011 by John Cronin
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:

 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.

 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

using System;
using System.Collections.Generic;
using System.Text;

namespace tysila
{
    partial class Assembler
    {
        TypeToCompile? tysos_field_ttc = null;
        Layout tysos_field_l = null;
        string tysos_field_ti = null;
        string tysos_field_vt = null;
        Dictionary<string, int> tysos_field_offsets = null;

        Layout GetTysosFieldLayout()
        {
            if (tysos_field_l == null)
            {
                tysos_field_ttc = Metadata.GetTTC("tysos", "tysos", "TysosField", this);
                tysos_field_l = Layout.GetLayout(tysos_field_ttc.Value, this);
                tysos_field_ti = Mangler.Mangle(tysos_field_ttc.Value, this) + "_TI";
                tysos_field_vt = Mangler.Mangle(tysos_field_ttc.Value, this) + "_VT";

                tysos_field_offsets = new Dictionary<string, int>();
                {
                    foreach (Layout.Field f in tysos_field_l.InstanceFields)
                    {
                        if (!tysos_field_offsets.ContainsKey(f.name))
                            tysos_field_offsets.Add(f.name, f.offset);
                    }
                }
            }

            return tysos_field_l;
        }

#if MT
        public void AssembleFieldInfo(object ftcr) { AssembleFieldInfo(ftcr as FTCRequest); }
        public void AssembleFieldInfo(Assembler.FTCRequest ftcr) { AssembleFieldInfo(ftcr.ftc, ftcr.of); }
#endif
        public void AssembleFieldInfo(Assembler.FieldToCompile ftc, IOutputFile of)
        {
            /* FieldInfo structs are based on tysos.FieldInfo (defined in the runtime/kernel)
             * 
             * They contain the following fields:
             * 
             * IntPtr __vtbl
             * Int32 __object_id
             * tysos.TysosType OwningType
             * tysos.TysosType _FieldType
             * String _Name
             * IntPtr Signature
             * IntPtr Sig_references
             * IntPtr Literal_data
             * Int32 Flags
             * 
             * With fields similar to TysosType
             */

            GetTysosFieldLayout();

            if (of == null)
            {
#if MT
                rtc--;
#endif
                return;
            }

            lock (of)
            {
                of.AlignData(GetSizeOfPointer());
                IList<byte> data_sect = of.GetData();
                int fi_top = data_sect.Count;
                string fi_name = Mangler.Mangle(ftc.DefinedIn, this) + Mangler.Mangle(ftc, this) + "_FI";

                Layout l = Layout.GetLayout(ftc.DefinedIn, this);
                Layout.Field f = l.GetField(Signature.GetString(ftc, this), ftc.field.IsStatic);

                of.AddDataSymbol(fi_top, fi_name);

                // Create the field info struct in memory
                byte[] fi = new byte[tysos_field_l.ClassSize];

                // __vtbl pointer
                of.AddDataRelocation(fi_top + tysos_field_offsets["IntPtr __vtbl"], "__tysos_field_vt", DataToDataRelocType(), 0);

                // __obj_id
                SetByteArray(fi, tysos_field_offsets["Int32 __object_id"], ToByteArraySignExtend(next_object_id.Increment, 4));

                // Owning type
                TypeToCompile owning_type = Metadata.GetTTC(new Token(ftc.field.owning_type), ftc.DefinedIn, this);
                of.AddDataRelocation(fi_top + tysos_field_offsets["tysos.TysosType OwningType"], Mangler.Mangle(owning_type, this) + "_TI", DataToDataRelocType(), 0);
                Requestor.RequestTypeInfo(owning_type);

                // _FieldType
                TypeToCompile field_type = Metadata.GetTTC(ftc.fsig, ftc.DefinedIn, null, this);
                of.AddDataRelocation(fi_top + tysos_field_offsets["tysos.TysosType _FieldType"], Mangler.Mangle(field_type, this) + "_TI", DataToDataRelocType(), 0);
                Requestor.RequestTypeInfo(field_type);

                // String _Name
                var v_n = ftc.definedin_type.m.StringTable.GetStringAddress(ftc.field.Name, this);
                of.AddDataRelocation(fi_top + tysos_field_offsets["String _Name"], v_n.base_var.v.label, DataToDataRelocType(), v_n.constant_offset);

                // Int32 Flags
                SetByteArray(fi, tysos_field_offsets["Int32 Flags"], ToByteArrayZeroExtend(ftc.field.Flags, 4));

                // Int32 offset
                SetByteArray(fi, tysos_field_offsets["Int32 offset"], ToByteArrayZeroExtend(f.offset, 4));


                // Now write the FieldInfo struct to the output file
                append_byte_array(data_sect, fi);


                // Write signature
                of.AlignData(GetSizeOfPointer());
                int signature_top = data_sect.Count;
                Dictionary<Metadata.TypeDefRow, byte> typedef_table = new Dictionary<Metadata.TypeDefRow, byte>();
                byte next_typedef_id = 0;
                write_ti_signature(data_sect, field_type, field_type.tsig.Type, typedef_table, ref next_typedef_id);
                of.AddDataRelocation(fi_top + tysos_field_offsets["IntPtr Signature"], fi_name, DataToDataRelocType(), signature_top - fi_top);

                // Write Sig_references
                of.AlignData(GetSizeOfPointer());
                int sigreferences_top = data_sect.Count;
                foreach (KeyValuePair<Metadata.TypeDefRow, byte> typedef_table_item in typedef_table)
                {
                    Assembler.TypeToCompile ref_ttc = Metadata.GetTTC(new Metadata.TableIndex(typedef_table_item.Key), field_type, this);
                    string ref_mangled_name = Mangler.Mangle(ref_ttc, this);
                    Requestor.RequestTypeInfo(ref_ttc);

                    of.AddDataRelocation(data_sect.Count, ref_mangled_name + "_TI", DataToDataRelocType(), 0);
                    WriteZeroIntPtr(of);
                }
                WriteZeroIntPtr(of);        // null terminate
                of.AddDataRelocation(fi_top + tysos_field_offsets["IntPtr Sig_references"], fi_name, DataToDataRelocType(), sigreferences_top - fi_top);

                // Write literal data
                if (ftc.field.LiteralData != null)
                {
                    of.AlignData(GetSizeOfPointer());
                    int litdata_top = data_sect.Count;
                    append_byte_array(data_sect, ftc.field.LiteralData);
                    of.AddDataRelocation(fi_top + tysos_field_offsets["IntPtr Literal_data"], fi_name, DataToDataRelocType(), litdata_top - fi_top);
                }
            }

#if MT
            rtc--;
#endif
        }

#if MT
        public void AssembleMethodInfo(object mtcr) { AssembleMethodInfo(mtcr as MTCRequest); }
        public void AssembleMethodInfo(Assembler.MTCRequest mtcr) { AssembleMethodInfo(mtcr.mtc, mtcr.of); }
#endif
        public void AssembleMethodInfo(Assembler.MethodToCompile mtc, IOutputFile of)
        {
            /* TODO */
            of.AddDataSymbol(of.GetData().Count, Mangler.Mangle(mtc, this) + "_MI");

#if MT
            rtc--;
#endif
        }
    }
}
